﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Fintech.Utils;

namespace ZOVNP.Model.Abstracts
{
    /// <summary> Атрибут для трансляции обёрточных свойств-перечислений в лямбда-выражениях в родные свойства сущностей. </summary>
    [AttributeUsage(AttributeTargets.Property)]
    public class EnumMappingAttribute : Attribute
    {
        /// <summary> Имя родного свойства сущности, использующегося для трансляции. </summary>
        public readonly string PropertyName;
        /// <summary> Стартовая позиция блока битов, отведённых под хранение значения перечисления. </summary>
        public readonly byte StartPosition;
        /// <summary> Длина блока битов, отведённых под хранение значения перечисления. </summary>
        public readonly byte Length;
        /// <summary> Числовой тип, используемый для трансляции. Должен соответствовать исходному типу поля. </summary>
        public readonly NumericType MappingType;
        /// <summary> Создать трансляционный атрибут для свойства-перечисления. </summary>
        /// <param name="PropertyName"> Имя родного свойства сущности. </param>
        /// <param name="MappingType"> Числовой тип, используемый для трансляции. </param>
        /// <param name="StartPosition"> Стартовая позиция блока битов, отведённых под хранение значения перечисления. </param>
        /// <param name="Length"> Длина блока битов, отведённых под хранение значения перечисления. </param>
        public EnumMappingAttribute(string PropertyName, NumericType MappingType, byte StartPosition = (byte)0, byte Length = (byte)64)
        {
            int overhead = (int)(StartPosition + Length) - (int)MappingType;
            if (overhead > 0)
                throw new ArgumentException(string.Format(Properties.Resources.MSG_EnumMappingOverflow, (int)MappingType), "MappingType/StartPosition/Length");
            this.PropertyName = PropertyName;
            this.StartPosition = StartPosition;
            this.Length = Length;
            this.MappingType = MappingType;
        }

        /// <summary> Создание битовой сплошной единичной маски, задающей место хранения значения транслируемого перечисления. </summary>
        public object MakeMask()
        {
            switch (MappingType)
            {
                case NumericType.Int64: return NumericManager.MakeMask64Signed(StartPosition, Length);
                case NumericType.UInt64: return NumericManager.MakeMask64(StartPosition, Length);
                case NumericType.SByte: return NumericManager.MakeMask8Signed(StartPosition, Length);
                case NumericType.Byte: return NumericManager.MakeMask8(StartPosition, Length);
                case NumericType.Int16: return NumericManager.MakeMask16Signed(StartPosition, Length);
                case NumericType.UInt16: return NumericManager.MakeMask16(StartPosition, Length);
                case NumericType.Int32: return NumericManager.MakeMask32Signed(StartPosition, Length);
                case NumericType.UInt32: return NumericManager.MakeMask32(StartPosition, Length);
                default: throw new NotSupportedException(string.Format(Properties.Resources.MSG_UnknownNumericType, MappingType));
            }
        }

        /// <summary> Получение числового значения специфического типа, смещённого на <c>StartPosition</c>. </summary>
        /// <returns> 1 в степени <c>StartPosition</c>. </returns>
        public object MakeShift()
        {
            switch (MappingType)
            {
                case NumericType.Int64: return (Int64)(1 << StartPosition);
                case NumericType.UInt64: return (UInt64)(1 << StartPosition);
                case NumericType.SByte: return (SByte)(1 << StartPosition);
                case NumericType.Byte: return (Byte)(1 << StartPosition);
                case NumericType.Int16: return (Int16)(1 << StartPosition);
                case NumericType.UInt16: return (UInt16)(1 << StartPosition);
                case NumericType.Int32: return (Int32)(1 << StartPosition);
                case NumericType.UInt32: return (UInt32)(1 << StartPosition);
                default: throw new NotSupportedException(string.Format(Properties.Resources.MSG_UnknownNumericType, MappingType));
            }
        }

        /// <summary> Преобразовать к строке. </summary>
        /// <returns> Строковое представление атрибута. </returns>
        public override string ToString() { return string.Format("Трансляция к свойству {0} типа {1}.", PropertyName, MappingType); }
    }

    /// <summary>
    /// Числовые типы, используемые при трансляции обёрточных свойств-перечислений
    /// в лямбда-выражениях в родные свойства сущностей.
    /// </summary>
    public enum NumericType
    {
        SByte = 7,
        /// <summary> 8 бит. </summary>
        Byte = 8,
        Int16 = 15,
        /// <summary> 16 бит. </summary>
        UInt16 = 16,
        Int32 = 31,
        /// <summary> 32 бит. </summary>
        UInt32 = 32,
        Int64 = 63,
        /// <summary> 64 бит. </summary>
        UInt64 = 64
    }
}
